﻿// Bing Maps ASP.NEt Control is a community project available under the Microsoft Public License (Ms-PL)
// Code is provided as is and with no warrenty – Use at your own risk
// View the project and the latest code at http://BingMapsASP.codeplex.com

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Web.Script.Serialization;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using Microsoft.Live.ServerControls.VE.Constants;
using Microsoft.Live.ServerControls.VE.ControllerLogic;
using Action=Microsoft.Live.ServerControls.VE.ControllerLogic.Action;

[assembly: WebResource("Microsoft.Live.ServerControls.VE.Map.js", "application/x-javascript")]
[assembly: WebResource("Microsoft.Live.ServerControls.VE.Map.debug.js", "application/x-javascript")]
namespace Microsoft.Live.ServerControls.VE
{
    ///<summary>
    /// The ASP.NET Windows Live Virtual Earth Control
    ///</summary>
    [Designer(typeof(MapDesigner))]
    [ToolboxBitmap(typeof(Map))]
    [ToolboxData(@"<{0}:Map ID=""Map1"" runat=""server"" ZoomLevel=""4"" Width=""400px"" Height=""400px"" ></{0}:Map>")]
    public class Map : ScriptControl, INamingContainer
    {

        #region Public Properties

        /// <summary>
        /// The width of the map
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Appearance)]
        [DefaultValue(400)]
        [Description("The width of the map")]
        public override Unit Width
        {
            get { return currentProperties.Width; }
            set 
            {
                base.Width = value;
                currentProperties.Width = value;
                PropertyChanged(ControlProperty.Width, value.Value);
            }
        }

        /// <summary>
        /// The height of the map
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Appearance)]
        [DefaultValue(400)]
        [Description("The height of the map")]
        public override Unit Height
        {
            get { return currentProperties.Height; }
            set
            {
                base.Height = value;
                currentProperties.Height = value;
                PropertyChanged(ControlProperty.Height, value.Value);
            }
        }

        public override string CssClass
        {
            get
            {
                return base.CssClass;
            }
            set
            {
                MapContainer.CssClass = value;
            }
        }

        /// <summary>
        /// Sets the view of the map to the specified lat/long
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.InitialView)]
        [DefaultValue("")]
        [Description("Sets the view of the map to the specified lat/long")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.Attribute)]
        public LatLong Center
        {
            get { return currentProperties.Center; }
            set
            {
                currentProperties.Center = value;
                PropertyChanged(ControlProperty.Center, value);
            }
        }

        /// <summary>
        /// A VEMapMode Enumeration value that specifies whether to load the map in 2D or 3D mode
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.InitialView)]
        [DefaultValue(MapMode.Mode2D)]
        [Description("A VEMapMode Enumeration value that specifies whether to load the map in 2D or 3D mode")]
        public MapMode MapMode
        {
            get { return currentProperties.MapMode; }
            set
            {
                currentProperties.MapMode = value;
                PropertyChanged(ControlProperty.MapMode, value);
            }
        }

        /// <summary>
        /// A VEMapStyle Enumeration value specifying the map style.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.InitialView)]
        [DefaultValue(MapStyle.Road)]
        [Description("A VEMapStyle Enumeration value specifying the map style")]
        public MapStyle MapStyle
        {
            get { return currentProperties.MapStyle; }
            set
            {
                currentProperties.MapStyle = value;
                PropertyChanged(ControlProperty.MapStyle, value.ToVEEnum());
            }
        }

        /// <summary>
        /// In 3D mode, shows the default user interface for controlling the map in 3D mode. By default, this control is shown
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(true)]
        [Description("In 3D mode, shows the default user interface for controlling the map in 3D mode. By default, this control is shown")]
        public bool NavigationControl3D
        {
            get { return currentProperties.NavigationControl3D; }
            set
            {
                currentProperties.NavigationControl3D = value;
                PropertyChanged(ControlProperty.NavigationControl3D, value);
            }
        }

        /// <summary>
        /// Shows the default user interface for controlling the map (the compass-and-zoom control). By default, this control is shown
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(true)]
        [Description("Shows the default user interface for controlling the map (the compass-and-zoom control). By default, this control is shown")]
        public bool Dashboard
        {
            get { return currentProperties.Dashboard; }
            set
            {
                currentProperties.Dashboard = value;
                PropertyChanged(ControlProperty.Dashboard, value);
            }
        }

        /// <summary>
        /// Sets the map dashboard size and type
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(DashboardSize.Normal)]
        [Description("Sets the map dashboard size and type")]
        public DashboardSize DashboardSize
        {
            get { return currentProperties.DashboardSize; }
            set
            {
                currentProperties.DashboardSize = value;
                PropertyChanged(ControlProperty.DashboardSize, value.ToVEEnum());
            }
        }

        /// <summary>
        /// Displays the mini map
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.MiniMap)]
        [DefaultValue(false)]
        [Description("Displays the mini map")]
        public bool MiniMap
        {
            get { return currentProperties.MiniMap; }
            set
            {
                currentProperties.MiniMap = value;
                PropertyChanged(ControlProperty.MiniMap, value);
            }
        }

        /// <summary>
        /// The x coordinate offset as a number of pixels from the top left corner of the screen.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.MiniMap)]
        [DefaultValue(90)]
        [Description("The x coordinate offset as a number of pixels from the top left corner of the screen.")]
        public int MiniMapXoffset
        {
            get { return currentProperties.MiniMapXoffset; }
            set
            {
                currentProperties.MiniMapXoffset = value;
                PropertyChanged(ControlProperty.MiniMapXoffset, value);
            }
        }

        /// <summary>
        /// The y coordinate offset as a number of pixels from the top left corner of the screen.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.MiniMap)]
        [DefaultValue(40)]
        [Description("The y coordinate offset as a number of pixels from the top left corner of the screen.")]
        public int MiniMapYoffset
        {
            get { return currentProperties.MiniMapYoffset; }
            set
            {
                currentProperties.MiniMapYoffset = value;
                PropertyChanged(ControlProperty.MiniMapYoffset, value);
            }
        }

        /// <summary>
        /// A VEMiniMapSize Enumeration value that specifies the mini map size. Optional. Default value is VEMiniMapSize.Small
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.MiniMap)]
        [DefaultValue(MiniMapSize.Small)]
        [Description("A VEMiniMapSize Enumeration value that specifies the mini map size. Optional. Default value is VEMiniMapSize.Small")]
        public MiniMapSize MiniMapSize
        {
            get { return currentProperties.MiniMapSize; }
            set
            {
                currentProperties.MiniMapSize = value;
                PropertyChanged(ControlProperty.MiniMapSize, value.ToVEEnum());
            }
        }

        /// <summary>
        /// Displays the traffic legend if traffic is enabled
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Traffic)]
        [DefaultValue(false)]
        [Description("Displays the traffic legend")]
        public bool TrafficLegend
        {
            get { return currentProperties.TrafficLegend; }
            set
            {
                currentProperties.TrafficLegend = value;
                PropertyChanged(ControlProperty.TrafficLegend, value);
            }
        }

        /// <summary>
        /// The x-coordinate of the top-left corner of the legend. Optional, -1 defaults to built in postion
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Traffic)]
        [DefaultValue(-1)]
        [Description("The x-coordinate of the top-left corner of the legend. Optional, -1 defaults to built in postion")]
        public int TrafficLegendX
        {
            get { return currentProperties.TrafficLegendX; }
            set
            {
                currentProperties.TrafficLegendX = value;
                PropertyChanged(ControlProperty.TrafficLegendX, value);
            }
        }

        /// <summary>
        /// The y-coordinate of the top-left corner of the legend. Optional, -1 defaults to built in postion
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Traffic)]
        [DefaultValue(-1)]
        [Description("The y-coordinate of the top-left corner of the legend. Optional, -1 defaults to built in postion")]
        public int TrafficLegendY
        {
            get { return currentProperties.TrafficLegendY; }
            set
            {
                currentProperties.TrafficLegendY = value;
                PropertyChanged(ControlProperty.TrafficLegendY, value);
            }
        }

        /// <summary>
        /// Loads the traffic information overlay on the map.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Traffic)]
        [DefaultValue(false)]
        [Description("Loads the traffic information overlay on the map")]
        public bool Traffic
        {
            get { return currentProperties.Traffic; }
            set
            {
                currentProperties.Traffic = value;
                PropertyChanged(ControlProperty.Traffic, value);
            }
        }

        /// <summary>
        /// Specifies whether shapes are drawn below a threshold zoom level
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Features)]
        [DefaultValue(true)]
        [Description("Specifies whether shapes are drawn below a threshold zoom level")]
        public bool EnableShapeDisplayThreshold
        {
            get { return currentProperties.EnableShapeDisplayThreshold; }
            set
            {
                currentProperties.EnableShapeDisplayThreshold = value;
                PropertyChanged(ControlProperty.EnableShapeDisplayThreshold, value);
            }
        }

        /// <summary>
        /// Specifies whether to zoom to the center of the screen instead of to the cursor position on the screen.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Features)]
        [DefaultValue(true)]
        [Description("Specifies whether to zoom to the center of the screen instead of to the cursor position on the screen.")]
        public bool MouseWheelZoomToCenter
        {
            get { return currentProperties.MouseWheelZoomToCenter; } 
            set 
            {
                currentProperties.MouseWheelZoomToCenter = value;
                PropertyChanged(ControlProperty.MouseWheelZoomToCenter, value);
            }
        }

        /// <summary>
        /// Sets the distance unit (kilometers or miles) for the map scale
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(DistanceUnit.Miles)]
        [Description("Sets the distance unit (kilometers or miles) for the map scale")]
        public DistanceUnit ScaleBarDistanceUnit
        {
            get { return currentProperties.ScaleBarDistanceUnit; }
            set
            {
                currentProperties.ScaleBarDistanceUnit = value;
                PropertyChanged(ControlProperty.ScaleBarDistanceUnit, value.ToVEEnum());
            }
        }

        /// <summary>
        /// Specifies the accuracy in converting shapes when the map style is changed to birdseye
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Birdseye)]
        [DefaultValue(ShapeAccuracy.None)]
        [Description("Specifies the accuracy in converting shapes when the map style is changed to birdseye")]
        public ShapeAccuracy ShapesAccuracy
        {
            get { return currentProperties.ShapesAccuracy; }
            set
            {
                currentProperties.ShapesAccuracy = value;
                PropertyChanged(ControlProperty.ShapesAccuracy, value);
            }
        }

        /// <summary>
        /// Specifies the maximum number of shapes that are accurately converted at one time when the map style is changed to birdseye
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Birdseye)]
        [DefaultValue(50)]
        [Description("Specifies the maximum number of shapes that are accurately converted at one time when the map style is changed to birdseye")]
        public int ShapesAccuracyRequestLimit
        {
            get { return currentProperties.ShapesAccuracyRequestLimit; }
            set
            {
                value = (value >= 0) ? value : 0;
                currentProperties.ShapesAccuracyRequestLimit = value;
                PropertyChanged(ControlProperty.ShapesAccuracyRequestLimit, value);
            }
        }

        /// <summary>
        /// Sets the number of "rings" of map tiles that should be loaded outside of the visible mapview area. This is also called tile overfetching
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Features)]
        [DefaultValue(0)]
        [Description("Sets the number of \"rings\" of map tiles that should be loaded outside of the visible mapview area. This is also called tile overfetching")]
        public int TileBuffer
        {
            get { return currentProperties.TileBuffer; }
            set
            {
                value = (value >= 0) ? value : 0;
                value = (value <= 3) ? value : 3;
                currentProperties.TileBuffer = value;
                PropertyChanged(ControlProperty.TileBuffer, value);
            }
        }

        /// <summary>
        /// Specifies the text shown with the traffic legend, if visible
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Traffic)]
        [Description("Specifies the text shown with the traffic legend, if visible")]
        public string TrafficLegendText
        {
            get { return currentProperties.TrafficLegendText; }
            set
            {
                currentProperties.TrafficLegendText = value;
                PropertyChanged(ControlProperty.TrafficLegendText, value);
            }
        }

        /// <summary>
        /// Sets the view of the map to the specified zoom level
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.InitialView)]
        [DefaultValue(4)]
        [Description("Sets the view of the map to the specified zoom level")]
        public int ZoomLevel
        {
            get { return currentProperties.ZoomLevel; }
            set
            {
                value = (value > 0) ? value : 1;
                value = (value < 22) ? value : 21;
                currentProperties.ZoomLevel = value;
                PropertyChanged(ControlProperty.ZoomLevel, value);
            }
        }

        /// <summary>
        /// Specifies whether the default disambiguation dialog is displayed when multiple results are returned from a location query using the VEMap.GetDirections Method
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(true)]
        [Description("Specifies whether the default disambiguation dialog is displayed when multiple results are returned from a location query using the VEMap.GetDirections Method")]
        public bool DisambiguationDialog
        {
            get { return currentProperties.DisambiguationDialog; }
            set
            {
                currentProperties.DisambiguationDialog = value;
                PropertyChanged(ControlProperty.DisambiguationDialog, value);
            }
        }

        /// <summary>
        /// The locale (language) feature to use where supported. Design time only.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Features)]
        [DefaultValue(SupportedLocales.NA)]
        [Description("The locale (language) feature to use where supported. Design time only.")]
        public SupportedLocales Locale
        {
            get { return currentProperties.Locale; }
            set
            {
                if (Runtime)
                {
                    ResourceManager rm = new ResourceManager("Microsoft.Live.ServerControls.VE.Map", Assembly.GetExecutingAssembly());
                    throw new System.Exception(rm.GetString(ExceptionProperty.ExceptionPropertyNoSetRunTime));
                }
                currentProperties.Locale = value;
                PropertyChanged(ControlProperty.Locale, value.ToVEEnum());
            }
        }

        /// <summary>
        /// Clears out all of the default Virtual Earth info box CSS styles
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(false)]
        [Description("Clears out all of the default Virtual Earth info box CSS styles")]
        public bool ClearInfoBoxStyles
        {
            get { return currentProperties.ClearInfoBoxStyles; }
            set
            {
                currentProperties.ClearInfoBoxStyles = value;
                PropertyChanged(ControlProperty.ClearInfoBoxStyles, value);
            }
        }

        /// <summary>
        /// A Boolean value that specifies whether the map view is displayed as a fixed map that the user cannot change. Optional. Default is false
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(false)]
        [Description("A Boolean value that specifies whether the map view is displayed as a fixed map that the user cannot change. Optional. Default is false")]
        public bool FixedMap
        {
            get { return currentProperties.FixedMap; }
            set 
            { 
                currentProperties.FixedMap = value;
                PropertyChanged(ControlProperty.FixedMap, value);
            }
        }

        /// <summary>
        /// A Boolean value that specifies whether to show the map mode switch on the dashboard control. Optional. Default is true (the switch is displayed).
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(true)]
        [Description("A Boolean value that specifies whether to show the map mode switch on the dashboard control. Optional. Default is true (the switch is displayed).")]
        public bool ShowMapModeSwitch
        {
            get { return currentProperties.ShowMapModeSwitch; }
            set 
            { 
                currentProperties.ShowMapModeSwitch = value;
                PropertyChanged(ControlProperty.ShowMapModeSwitch, value);
            }
        }

        /// <summary>
        /// A Boolean value that specifies whether to show the built in Find Control.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Controls)]
        [DefaultValue(false)]
        [Description("A Boolean value that specifies whether to show the built in Find Control")]
        public bool ShowFindControl
        {
            get { return currentProperties.ShowFindControl; }
            set
            {
                currentProperties.ShowFindControl = value;
                PropertyChanged(ControlProperty.ShowFindControl, value);
            }
        }


        /// <summary>
        /// a double that represents the altitude (in meters) above the geoid.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.InitialView)]
        [DefaultValue(0.0)]
        [Description("a double that represents the altitude (in meters) above the geoid.")]
        public double Altitude 
        {
            get { return currentProperties.Altitude; }
            set
            {
                value = (value >= 0) ? value : 0;
                currentProperties.Altitude = value;
                PropertyChanged(ControlProperty.Altitude, value);
            }
        }

        /// <summary>
        /// a double that represents the compass heading of the current map view.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.InitialView)]
        [DefaultValue(0.0)]
        [Description("a double that represents the compass heading of the current map view.")]
        public double Heading
        {
            get { return currentProperties.Heading; }
            set
            {
                currentProperties.Heading = value;
                PropertyChanged(ControlProperty.Heading, value);
            }
        }

        /// <summary>
        /// a double that represents the pitch of the current map view.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.InitialView)]
        [DefaultValue(-90.0)]
        [Description("a double that represents the pitch of the current map view.")]
        public double Pitch
        {
            get { return currentProperties.Pitch; }
            set
            {
                value = (value >= -90) ? value : -90;
                value = (value <= 0) ? value : 0;
                currentProperties.Pitch = value;
                PropertyChanged(ControlProperty.Pitch, value);
            }
        }

        /// <summary>
        /// Specifies what the map control does when a request to the server to get the accurate position of a shape when the map style is changed to birdseye fails.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Birdseye)]
        [DefaultValue(FailedShapeRequest.DrawInaccurately)]
        [Description("Specifies what the map control does when a request to the server to get the accurate position of a shape when the map style is changed to birdseye fails.")]
        public FailedShapeRequest FailedShapeRequest
        {
            get { return currentProperties.FailedShapeRequest; }
            set
            {
                currentProperties.FailedShapeRequest = value;
                PropertyChanged(ControlProperty.FailedShapeRequest, value);
            }
        }

        /// <summary>
        /// Use the HTTPS version of the control when required. Design time only.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Features)]
        [DefaultValue(false)]
        [Description("Use the HTTPS version of the control when required. Design time only.")]
        public bool HTTPS
        {
            get { return currentProperties.HTTPS; }
            set
            {
                if (Runtime)
                {
                    ResourceManager rm = new ResourceManager("Microsoft.Live.ServerControls.VE.Map", Assembly.GetExecutingAssembly());
                    throw new System.Exception(rm.GetString(ExceptionProperty.ExceptionPropertyNoSetRunTime));
                }
                currentProperties.HTTPS = value;
            }
        }

        /// <summary>
        /// The unique client token from the Virtual Earth Platform service.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Tracking)]
        [DefaultValue("")]
        [Description("The unique client token from the Virtual Earth Platform service")]
        public string ClientToken
        {
            get { return currentProperties.ClientToken; }
            set
            {
                currentProperties.ClientToken = value;
                PropertyChanged(ControlProperty.ClientToken, value);
            }

        }

        /// <summary>
        /// Shapes, layers and custom tiles are passed to the server. This will increase pagesize from client to server allowing access to all Data and persisting on postback.
        /// </summary>
        [Browsable(true)]
        [Category(ControlCategory.Features)]
        [DefaultValue(false)]
        [Description("Shapes, layers and custom tiles are passed to the server. This will increase pagesize from client to server allowing access to all Data and persisting on postback.")]
        public bool SendLayersToServer
        {
            get { return currentProperties.SendLayersToServer; }
            set {
                currentProperties.SendLayersToServer = value;
                PropertyChanged(ControlProperty.SendLayersToServer, value);
            }
        }

        #endregion

        #region Hidden Base Properties

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override bool EnableTheming
        {
            get
            {
                return base.EnableTheming;
            }
            set { }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override FontInfo Font
        {
            get
            {
                return base.Font;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string SkinID
        {
            get
            {
                return base.SkinID;
            }
            set { }
        }


        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string AccessKey
        {
            get
            {
                return base.AccessKey;
            }
            set { }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override System.Drawing.Color BorderColor
        {
            get
            {
                return base.BorderColor;
            }
            set { }

        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override System.Drawing.Color BackColor
        {
            get
            {
                return base.BackColor;
            }
            set { }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override BorderStyle BorderStyle
        {
            get
            {
                return base.BorderStyle;
            }
            set { }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override Unit BorderWidth
        {
            get
            {
                return base.BorderWidth;
            }
            set { }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override System.Drawing.Color ForeColor
        {
            get
            {
                return base.ForeColor;
            }
            set { }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override short TabIndex
        {
            get
            {
                return base.TabIndex;
            }
            set { }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string ToolTip
        {
            get
            {
                return base.ToolTip;
            }
            set { }
        }

        #endregion

        #region Read Only Properties

        private bool Runtime;
        private bool isBirdseyeAvailable;
        private BirdseyeScene currentBirdseyeScene;
        private LatLongRectangle mapView;

        /// <summary>
        /// Determines whether the bird's eye map style is available in the current map view
        /// </summary>
        public bool IsBirdseyeAvailable
        {
            get { return isBirdseyeAvailable; }
        }

        /// <summary>
        /// If the map view is already set to bird's eye, returns the current VEBirdseyeScene Class object, else null.
        /// </summary>
        public BirdseyeScene CurrentBirdseyeScene
        {
            get { return currentBirdseyeScene; }
        }

        /// <summary>
        /// Returns the current map view object as a VELatLongRectangle Class object
        /// </summary>
        public LatLongRectangle MapView
        {
            get { return mapView; }
        }
        #endregion

        #region Data Storage

        private ClientData clientData;

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds a VEShape Class object or array of VEShape pushpin objects to the base layer
        /// </summary>
        /// <param name="shape">The VEShape object or array of VEShape pushpin objects to be added</param>
        public void AddShape(Shape shape) 
        {
            clientData.AddShape(shape);
        }

        /// <summary>
        /// Adds the specified shape layer to the map
        /// </summary>
        /// <param name="layer">A reference to the layer to add</param>
        public void AddShapeLayer(ShapeLayer layer) 
        {
            clientData.AddShapeLayer(layer);
        }


        /// <summary>
        /// Adds a tile layer to the map, and if the visibleOnLoad parameter is true, it also shows it on the map
        /// </summary>
        /// <param name="layerSource">The VETileSourceSpecification Class object representing the source of the tile layer</param>
        public void AddTileLayer(TileSourceSpecification layerSource)
        {
            clientData.AddTileLayer(layerSource);
        }

        /// <summary>
        /// Adds a tile layer to the map, and if the visibleOnLoad parameter is true, it also shows it on the map
        /// </summary>
        /// <param name="layerSource">The VETileSourceSpecification Class object representing the source of the tile layer</param>
        /// <param name="visible">Is the layer visile on load</param>
        public void AddTileLayer(TileSourceSpecification layerSource, bool visible)
        {
            clientData.AddTileLayer(layerSource, visible);
        }

        /// <summary>
        /// Resizes the map to fit the div tag that it is contained in
        /// </summary>
        public void AutoResize()
        {
            Actions.AddAction(new Action { MethodName = "AutoResize" });
        }

        /// <summary>
        /// Removes all shapes, shape layers, and search results on the map. Also removes the route from the map, if one is displayed
        /// </summary>
        public void Clear() 
        {
            clientData.ClearShapes();
            //not documented by API traffic is cleared.
            Traffic = false;
        }

        /// <summary>
        /// Deletes all shape layers, along with any shapes within the layers
        /// </summary>
        public void DeleteAllShapeLayers() 
        {
            clientData.DeleteAllShapeLayers();
        }

        /// <summary>
        /// Deletes all shapes in all layers, leaving empty layers behind
        /// </summary>
        public void DeleteAllShapes() 
        {
            clientData.DeleteAllShapes();
        }

        /// <summary>
        /// Clears the current route (VERoute Class object) from the map
        /// </summary>
        public void DeleteRoute() 
        {
            Actions.AddAction(new Action { MethodName = "DeleteRoute" });
        }

        /// <summary>
        /// Deletes a VEShape Class object from any layer, including the base map layer
        /// </summary>
        /// <param name="shape"></param>
        public void DeleteShape(Shape shape) 
        {
            clientData.DeleteShape(shape);
        }

        /// <summary>
        /// Deletes the specified shape layer from the map
        /// </summary>
        /// <param name="layerID">A reference to the shape layer to delete</param>
        public void DeleteShapeLayer(string layerID) 
        {
            clientData.DeleteShapeLayer(layerID);
        }

        /// <summary>
        /// Deletes the specified tile layer from the map
        /// </summary>
        /// <param name="layerID">A reference to the tile layer to delete</param>
        public void DeleteTileLayer(string layerID)
        {
            clientData.DeleteTileLayer(layerID);           
        }
        
        /// <summary>
        /// Stops the continuous map panning initiated by a call to the VEMap.StartContinuousPan Method
        /// </summary>                 
        public void EndContinuousPan() 
        {
            Actions.AddAction(new Action { MethodName = "EndContinuousPan" });
        }
        
        /// <summary>
        /// Performs a what (business) search or a where (location) search. At least one of these two parameters is required
        /// </summary>
        /// <param name="what">The business name, category, or other item for which the search is conducted. This parameter must be supplied for a pushpin to be included in the results</param>
        /// <param name="where">The address or place name of the area for which the search is conducted. This parameter is overloaded; see the Remarks section for more information</param>
        /// <param name="findType">A VEFindType Enumeration value that specifies the type of search performed. The only currently supported value is VEFindType.Businesses</param>
        /// <param name="shapeLayer">A reference to the VEShapeLayer Class object that contain the pins that result from this search if a what parameter is specified. Optional. If the shape layer is not specified, the pins are added to the base map layer. If the reference is not a valid VEShapeLayer reference, an exception is thrown.</param>
        /// <param name="startIndex">The beginning index of the results returned. Optional. Default is 0.</param>
        /// <param name="numberOfResults">The number of results to be returned, starting at startIndex. The default is 10, the minimum is 1, and the maximum is 20</param>
        /// <param name="showResults">A Boolean value that specifies whether the resulting pushpins are visible. Optional. Default is true</param>
        /// <param name="createResults">A Boolean value that specifies whether pushpins are created when a what parameter is supplied</param>
        /// <param name="useDefaultDisambiguation">A Boolean value that specifies whether the map control displays a disambiguation box when multiple location matches are possible. If true, the map control displays a disambiguation box</param>
        /// <param name="setBestMapView">A Boolean value that specifies whether the map control moves the view to the first location match</param>
        public void Find(string what, string where, FindType findType, ShapeLayer shapeLayer, int startIndex, int numberOfResults, bool showResults, bool createResults, bool useDefaultDisambiguation,  bool setBestMapView) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(what);
            parameters.Add(where);
            parameters.Add(findType.ToVEEnum());
            parameters.Add((shapeLayer != null) ? shapeLayer.ID : "");
            parameters.Add(startIndex);
            parameters.Add(numberOfResults);
            parameters.Add(showResults);
            parameters.Add(createResults);
            parameters.Add(useDefaultDisambiguation);
            parameters.Add(setBestMapView);
            Actions.AddAction(new Action { MethodName = "Find", Params = parameters });
        }

        /// <summary>
        /// Performs a what (business) search or a where (location) search. At least one of these two parameters is required
        /// </summary>
        /// <param name="what">The business name, category, or other item for which the search is conducted. This parameter must be supplied for a pushpin to be included in the results</param>
        /// <param name="where">The address or place name of the area for which the search is conducted. This parameter is overloaded; see the Remarks section for more information</param>
        public void Find(string what, string where)
        {
            List<object> parameters = new List<object>();
            parameters.Add(what);
            parameters.Add(where);
            Actions.AddAction(new Action { MethodName = "Find", Params = parameters });
        }

        /// <summary>
        /// If the map view is already set to bird's eye, returns the current VEBirdseyeScene Class object
        /// </summary>
        /// <returns>Returns null if the map mode is set to 3D (VEMap.GetMapMode Method returns 2). Otherwise returns a VEBirdseyeScene Class object that represents the current bird's eye image</returns>
        public BirdseyeScene GetBirdseyeScene()
        {
            return CurrentBirdseyeScene;
        }

        /// <summary>
        /// Draws a multi-point route on the map and sends details about the route to a callback function
        /// </summary>
        /// <param name="locations">An array of objects specifying the points through which the route must pass. The points can be either VELatLong Class objects or String objects</param>
        /// <param name="options">A VERouteOptions Class object specifying the routing options.</param>                          
        public void GetDirections(List<string> locations, RouteOptions options) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(locations);
            parameters.Add(options.DistanceUnit.ToVEEnum());
            parameters.Add(options.DrawRoute);
            parameters.Add(options.RouteColor);
            parameters.Add(options.RouteOptimize.ToVEEnum());
            parameters.Add(options.RouteWeight);
            parameters.Add(options.RouteZIndex);
            parameters.Add(options.SetBestMapView);
            parameters.Add(options.ShowDisambiguation);
            parameters.Add(options.ShowErrorMessages);
            parameters.Add(options.UseMWS);
            Actions.AddAction(new Action { MethodName = "GetDirections", Params = parameters });
        }

        /// <summary>
        /// Draws a multi-point route on the map and sends details about the route to a callback function
        /// </summary>
        /// <param name="locations">An array of objects specifying the points through which the route must pass. The points can be either VELatLong Class objects or String objects</param>
        /// <param name="options">A VERouteOptions Class object specifying the routing options.</param>                          
        public void GetDirections(List<LatLongWithAltitude> locations, RouteOptions options)
        {
            List<object> parameters = new List<object>();
            parameters.Add(locations);
            parameters.Add(options.DistanceUnit.ToVEEnum());
            parameters.Add(options.DrawRoute);
            parameters.Add(options.RouteColor);
            parameters.Add(options.RouteOptimize.ToVEEnum());
            parameters.Add(options.RouteWeight);
            parameters.Add(options.RouteZIndex);
            parameters.Add(options.SetBestMapView);
            parameters.Add(options.ShowDisambiguation);
            parameters.Add(options.ShowErrorMessages);
            parameters.Add(options.UseMWS);
            Actions.AddAction(new Action { MethodName = "GetDirections", Params = parameters });
        }

        /// <summary>
        /// Gets the reference to a VEShape Class object based on its internal identifier
        /// </summary>
        /// <param name="shapeID">The identifier of the shape to retrieve</param>
        /// <returns>A reference to the specified VEShape object. If no shape exists with the specified ID, this method returns null</returns>
        public Shape GetShapeByID(string shapeID) 
        {
            return clientData.GetShapeByID(shapeID) ;
        }
        
        /// <summary>
        /// Gets a shape layer based upon an index value
        /// </summary>
        /// <param name="index">The index of the layer that you wish to retrieve</param>
        /// <returns>A reference to the requested layer. If there is not a valid shape layer at the specified index, null is returned</returns>
        public ShapeLayer GetShapeLayerByIndex(int index) 
        {
            return clientData.GetShapeLayerByIndex(index);
        }
          
        /// <summary>
        /// Gets the total number of shape layers on the map
        /// </summary>
        /// <returns>An integer representing the total number of shape layers on the map</returns>
        public int GetShapeLayerCount() 
        {
            return clientData.ShapeLayers.Count;
        }
        
        /// <summary>
        /// Gets a tile layer based upon its identifier
        /// </summary>
        /// <param name="layerID">The unique identifier of the tile layer</param>
        /// <returns>A VETileSourceSpecification Class object</returns>
        public TileSourceSpecification GetTileLayerByID(string layerID) 
        {
            return clientData.GetTileLayerByID(layerID);
        }

        /// <summary>
        /// Gets a tile layer based upon an index value
        /// </summary>
        /// <param name="index">The index into the list of tile layers. The value ranges from 0 to GetTileLayerCount</param>
        /// <returns>A VETileSourceSpecification Class object</returns>
        public TileSourceSpecification GetTileLayerByIndex(int index) 
        {
            return clientData.GetTileLayerByIndex(index);
        }
           
        /// <summary>
        /// Gets the number of tile layers
        /// </summary>
        /// <returns>An integer specifying the number of tile layers</returns>
        public int GetTileLayerCount() 
        {
            return clientData.GetTileLayerCount();
        }
        
        /// <summary>
        /// Hides all of the shape layers on the map
        /// </summary>
        public void HideAllShapeLayers() {
            clientData.HideAllShapeLayers();
        }
        
        /// <summary>
        /// Hides a tile layer from view
        /// </summary>
        /// <param name="layerID">The ID of the layer to be hidden</param>
        public void HideTileLayer(string layerID) 
        {
            clientData.HideTileLayer(layerID);
        } 
           
        /// <summary>
        /// Imports data from a GeoRSS feed, Live Search Maps (http://maps.live.com) collection, or KML URL
        /// </summary>
        /// <param name="shapeSource">A VEShapeSourceSpecification Class object specifying the imported shape data</param>
        /// <param name="callback">The name of the javscript method to call on completion</param>
        /// <param name="setBestView">A Boolean value that specifies whether the map view is changed to the best view for the layer</param>
        public void ImportShapeLayerData(ShapeSourceSpecification shapeSource, string callback, bool setBestView) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(shapeSource.Layer);
            parameters.Add(shapeSource.LayerSource);
            parameters.Add(shapeSource.Type.ToVEEnum());
            parameters.Add(callback);
            parameters.Add(setBestView);
            Actions.AddAction(new Action { MethodName = "ImportShapeLayerData", Params = parameters });
        } 
        
        /// <summary>
        /// Changes the map view so that it includes both the specified VELatLong Class point and the center point of the current map
        /// </summary>
        /// <param name="latlong">A VELatLong Class object that specifies the latitude and longitude of the point to include</param>
        public void IncludePointInView(LatLongWithAltitude latlong) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(latlong);
            Actions.AddAction(new Action { MethodName = "IncludePointInView", Params = parameters });
        } 
          
        /// <summary>
        /// When in 2D mode, moves the map the specified amount
        /// </summary>
        /// <param name="deltaX">The amount to move the map horizontally, in pixels</param>
        /// <param name="deltaY">The amount to move the map vertically, in pixels</param>
        public void Pan(int deltaX, int deltaY)
        {
            List<object> parameters = new List<object>();
            parameters.Add(deltaX);
            parameters.Add(deltaY);
            Actions.AddAction(new Action { MethodName = "Pan", Params = parameters });
        }
                                                     
        /// <summary>
        /// Pans the map to a specific latitude and longitude
        /// </summary>
        /// <param name="latlong">A VELatLong Class object that represents the latitude and longitude of the point on which to center the map</param>
        public void PanToLatLong(LatLongWithAltitude latlong) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(latlong);
            Actions.AddAction(new Action { MethodName = "PanToLatLong", Params = parameters });
        } 
        
        /// <summary>
        /// Resizes the map based on the specified width and height
        /// </summary>
        /// <param name="width">The width, in pixels, of the map</param>
        /// <param name="height">The height, in pixels, of the map</param>
        public void Resize(int width, int height) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(width);
            parameters.Add(height);
            Actions.AddAction(new Action { MethodName = "Resize", Params = parameters });
        }

        /// <summary>
        /// Changes the orientation of the existing bird's eye image (VEBirdseyeScene Class object) to the specified orientation
        /// </summary>
        /// <param name="orientation">VEOrientation Enumeration</param>
        /// <returns>Returns false if the map mode is set to 3D</returns>
        public bool SetBirdseyeOrientation(Orientation orientation) 
        {
            if (MapMode == VE.MapMode.Mode2D) {
                List<object> parameters = new List<object>();
                parameters.Add(orientation.ToVEEnum());
                Actions.AddAction(new Action { MethodName = "SetBirdseyeOrientation", Params = parameters });
                return true;
            }else
            {
                return false;
            }
        } 
        
        /// <summary>
        /// Displays the specified bird's eye image
        /// </summary>
        /// <param name="latlong">A VELatLong Class object specifying the center of the image. Optional. If this parameter is not supplied the center of the map is used</param>
        /// <param name="orientation">A VEOrientation Enumeration value specifying the direction to which which the image is viewed</param>
        /// <param name="zoomLevel">The level of zoom. Optional. If this parameter is not supplied, the value 1 is used</param>
        /// <param name="callback">The name of the function called when the SetBirdseyeScene method completes</param>
        public void SetBirdseyeScene(LatLongWithAltitude latlong, Orientation orientation, int zoomLevel, string callback) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(latlong);
            parameters.Add(orientation.ToVEEnum());
            parameters.Add(zoomLevel);
            parameters.Add(callback);
            Actions.AddAction(new Action { MethodName = "SetBirdseyeScene", Params = parameters });
        } 

        /// <summary>
        /// Displays the bird's eye image specified by the VEBirdseyeScene Class ID
        /// </summary>
        /// <param name="sceneID">The ID of the VEBirdseyeScene Class object that you want to display</param>
        public void SetBirdseyeScene(string sceneID) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(sceneID);
            Actions.AddAction(new Action { MethodName = "SetBirdseyeScene", Params = parameters });
        } 

        /// <summary>
        /// Sets the map view specified by a VEMapViewSpecification Class object
        /// </summary>
        /// <param name="mapViewSpecification">The exact view to change too</param>
        public void SetMapView(MapViewSpecification mapViewSpecification)
        {
            Altitude = mapViewSpecification.Altitude;
            Center = mapViewSpecification.Center;
            Heading = mapViewSpecification.Heading;
            Pitch = mapViewSpecification.Pitch;
            ZoomLevel = mapViewSpecification.Zoom;
        }

        /// <summary>
        /// Sets the map view specified by a List of LatLong values
        /// </summary>
        /// <param name="locs">A list of LatLong</param>
        public void SetMapView(List<LatLongWithAltitude> locs)
        {
            List<object> parameters = new List<object>();
            parameters.Add(locs);
            Actions.AddAction(new Action { MethodName = "SetMapView", Params = parameters });
        }

        /// <summary>
        /// Sets the map view specified by a LatLong Rectangle
        /// </summary>
        /// <param name="rect">A Lat Long Rectangle object</param>
        public void SetMapView(LatLongRectangle rect)
        {
            List<object> parameters = new List<object>();
            parameters.Add(rect);
            Actions.AddAction(new Action { MethodName = "SetMapView", Params = parameters });
        }

        /// <summary>
        /// Shows all shape layers on the map
        /// </summary>
        public void ShowAllShapeLayers() 
        {
            clientData.ShowAllShapeLayers();
        }
        
        /// <summary>
        /// Displays the specified message in a dialog box on the map
        /// </summary>
        /// <param name="message">The message you want to display on the map</param>
        public void ShowMessage(string message) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(message);
            Actions.AddAction(new Action { MethodName = "ShowMessage", Params = parameters });
        }
 
        /// <summary>
        /// Shows a tile layer on the map
        /// </summary>
        /// <param name="layerID">The ID of the layer to be shown</param>
        public void ShowTileLayer(string layerID) 
        {
            clientData.ShowTileLayer(layerID);
        }                                                       
         
        /// <summary>
        /// Moves the map in the specified direction until the VEMap.EndContinuousPan Method is called
        /// </summary>
        /// <param name="x">The speed, as a percentage of the fastest speed, to move the map in the x direction. Positive numbers move the map to the right, while negative numbers move the map to the left</param>
        /// <param name="y">The speed, as a percentage of the fastest speed, to move the map in the y direction. Positive numbers move the map down, while negative numbers move the map up</param>
        public void StartContinuousPan(int x, int y) 
        {
            List<object> parameters = new List<object>();
            parameters.Add(x);
            parameters.Add(y);
            Actions.AddAction(new Action { MethodName = "StartContinuousPan", Params = parameters });
        }                                                                                                                                                                                             

        /// <summary>
        /// Increases the map zoom level by 1
        /// </summary>
        public void ZoomIn()
        {
            ZoomLevel = ZoomLevel + 1;
        }

        /// <summary>
        /// Decreases the map zoom level by 1
        /// </summary>
        public void ZoomOut()
        {
            ZoomLevel = ZoomLevel - 1;
        }

        /// <summary>
        /// Gets the reference to the layer containing the specified VEShape object.
        /// </summary>
        /// <param name="shape">The shape you wish t find the layer for</param>
        /// <returns>Returns a reference to the layer containing the specified VEShape object. If the VEShape object is not contained in any layer, null is returned</returns>
        public ShapeLayer GetShapeLayerByShape(Shape shape)
        {
            return clientData.GetShapeLayerByShape(shape);
        }

        #endregion

        #region Constructor
        public Map()
        {
            Actions = new ActionController();
            propertyChanges = new Dictionary<string, string>();
            PropertyChanged += OnPropertyChanged;
            eventCount = new Dictionary<string, int>();
            currentProperties = new CurrentProperties();

            clientData = new ClientData();
        }
        #endregion

        #region Control State Logic
        private CurrentProperties currentProperties;

        [Serializable]
        private class CurrentProperties
        {
            //storage for property values and default values
            public Unit Width = Unit.Pixel(400);
            public Unit Height = Unit.Pixel(400);
            public LatLong Center = new LatLong(40.0, -104.0);
            public MapMode MapMode = MapMode.Mode2D;
            public MapStyle MapStyle = MapStyle.Road;
            public bool NavigationControl3D = true;
            public bool Dashboard = true;
            public DashboardSize DashboardSize = DashboardSize.Normal;
            public bool MiniMap = false;
            public int MiniMapXoffset = 90;
            public int MiniMapYoffset = 40;
            public MiniMapSize MiniMapSize = MiniMapSize.Small;
            public bool TrafficLegend = false;
            public int TrafficLegendX = -1;
            public int TrafficLegendY = -1;
            public bool Traffic = false;
            public bool EnableShapeDisplayThreshold = true;
            public bool MouseWheelZoomToCenter = true;
            public DistanceUnit ScaleBarDistanceUnit = DistanceUnit.Miles;
            public ShapeAccuracy ShapesAccuracy = ShapeAccuracy.None;
            public int ShapesAccuracyRequestLimit = 50;
            public int TileBuffer = 0;
            public string TrafficLegendText;
            public int ZoomLevel = 4;
            public bool DisambiguationDialog = true;
            public SupportedLocales Locale = SupportedLocales.NA;
            public bool ClearInfoBoxStyles = false;
            public bool FixedMap = false;
            public bool ShowMapModeSwitch = true;
            public bool ShowFindControl = false;
            public double Altitude = 0.0;
            public double Heading = 0.0;
            public double Pitch = -90.0;
            public FailedShapeRequest FailedShapeRequest = FailedShapeRequest.DrawInaccurately;
            public bool HTTPS = false;
            public string ClientToken = "";
            public bool SendLayersToServer = false;

            public string OnClientLoadMap = ScriptEvent.Default;
            public string OnClientChangeMapStyle = ScriptEvent.Default;
            public string OnClientChangeView = ScriptEvent.Default;
            public string OnClientEndPan = ScriptEvent.Default;
            public string OnClientEndZoom = ScriptEvent.Default;
            public string OnClientError = ScriptEvent.Default;
            // Issue:27368 - Removed since this event is to be disabled/removed from the api
            //public string OnClientInitMode = ScriptEvent.Default;
            public string OnClientModeNotAvailable = ScriptEvent.Default;
            public string OnClientObliqueChange = ScriptEvent.Default;
            public string OnClientObliqueEnter = ScriptEvent.Default;
            public string OnClientObliqueLeave = ScriptEvent.Default;
            public string OnClientResize = ScriptEvent.Default;
            public string OnClientStartPan = ScriptEvent.Default;
            public string OnClientStartZoom = ScriptEvent.Default;
            public string OnClientKeyPress = ScriptEvent.Default;
            public string OnClientKeyDown = ScriptEvent.Default;
            public string OnClientKeyUp = ScriptEvent.Default;
            public string OnClientClick = ScriptEvent.Default;
            public string OnClientDoubleClick = ScriptEvent.Default;
            public string OnClientMouseDown = ScriptEvent.Default;
            public string OnClientMouseMove = ScriptEvent.Default;
            public string OnClientMouseOut = ScriptEvent.Default;
            public string OnClientMouseOver = ScriptEvent.Default;
            public string OnClientMouseUp = ScriptEvent.Default;
            public string OnClientMouseWheel = ScriptEvent.Default;
            public string OnClientFind = ScriptEvent.Default;
            public string OnClientGetDirections = ScriptEvent.Default;
            public string OnClientTokenExpire = ScriptEvent.Default;
            public string OnClientTokenError = ScriptEvent.Default;
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            Page.RegisterRequiresControlState(this);
        }

        protected override object SaveControlState()
        {
            return currentProperties;
        }

        protected override void LoadControlState(object state)
        {
            if (state != null)
            {
                currentProperties = (CurrentProperties)state;
            }else
            {
                currentProperties = new CurrentProperties();
            }
        }

        #endregion

        #region Private Instance Property(s)

        private Panel MapContainer { get; set; }
        private HtmlInputHidden MapData { get; set; }
        private HtmlInputHidden CommsFlag { get; set; }
        private HtmlInputHidden MapChanges { get; set; }
        private HtmlInputHidden Commands { get; set; }
        private UpdatePanel UpdatePanel { get; set; }
        private HiddenField PanelTrigger { get; set; }
        private ActionController Actions { get; set; }

        #endregion

        #region Private events

        private delegate void PropertyChangedHandler(string name, object clientValue);
        private event PropertyChangedHandler PropertyChanged;

        private Dictionary<string, string> propertyChanges;

        protected void OnPropertyChanged(string name, object clientValue)
        {
            JavaScriptSerializer jss = new JavaScriptSerializer();
            string value = jss.Serialize(clientValue);
            if (propertyChanges.ContainsKey(name))
            {
                propertyChanges[name] = value;
            }else
            {
                propertyChanges.Add(name, value);
            }
        }

        /// <summary>
        /// Receives our communication from the client side code
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void UpdatePanel_Load(object sender, EventArgs e)
        {
            //sync map properties from clientside diff
            SetPropertiesFromJSON(ReplaceEnumStringsWithInts(MapChanges.Value));
            //reset changes
            propertyChanges = new Dictionary<string, string>();
            //trigger no chnages to designtime properties
            Runtime = true;

            //load data from client
            clientData.SetDataFromJSON(ReplaceEnumStringsWithInts(MapData.Value));

            string messageData = ReplaceEnumStringsWithInts(PanelTrigger.Value);

            //if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
            if (messageData.Length > 0)
            {
                JavaScriptSerializer jss = new JavaScriptSerializer();
                Dictionary<string, string> messages = jss.Deserialize<Dictionary<string, string>>(messageData);
                foreach (KeyValuePair<string, string> message in messages)
                {
                    switch (message.Key)
                    {
                        case ScriptEvent.OnClientLoadMap:
                            OnServerLoadMap(EventArgs.Empty);
                            break;
                        case ScriptEvent.OnClientChangeMapStyle:
                            OnServerChangeMapStyle(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientChangeView:
                            OnServerChangeView(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientEndPan:
                            OnServerEndPan(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientEndZoom:
                            OnServerEndZoom(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientError:
                            OnServerError(getArgs(message.Value));
                            break;
                        // Issue:27368 - Removed since this event is to be disabled/removed from the api
                        //case ScriptEvent.OnClientInitMode:
                        //    OnServerInitMode(getArgs(message.Value));
                        //    break;
                        case ScriptEvent.OnClientModeNotAvailable:
                            OnServerModeNotAvailable(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientObliqueChange:
                            OnServerObliqueChange(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientObliqueEnter:
                            OnServerObliqueEnter(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientObliqueLeave:
                            OnServerObliqueLeave(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientResize:
                            OnServerResize(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientClick:
                            OnServerClick(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientDoubleClick:
                            OnServerDoubleClick(getArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientFind:
                            OnServerFind(getFindArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientGetDirections:
                            OnServerGetDirections(getRouteArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientTokenExpire:
                            OnServerTokenExpire(getRouteArgs(message.Value));
                            break;
                        case ScriptEvent.OnClientTokenError:
                            OnServerTokenError(getRouteArgs(message.Value));
                            break;
                    }
                }
            }

            PanelTrigger.Value = string.Empty;
            Commands.Value = string.Empty;
            UpdatePanel.Update();
        }

        #endregion

        #region client side events

        /// <summary>
        /// This function receives notifications when map loads.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when map loads.")]
        public string OnClientLoadMap
        {
            get
            {
                return currentProperties.OnClientLoadMap;
            }
            set
            {
                currentProperties.OnClientLoadMap = value;
            }
        }

        /// <summary>
        /// Occurs when the map style changes
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("Occurs when the map style changes.")]
        public string OnClientChangeMapStyle
        {
            get
            {
                return currentProperties.OnClientChangeMapStyle;
            }
            set
            {
                currentProperties.OnClientChangeMapStyle = value;
            }
        }

        /// <summary>
        /// This function receives notifications when map changes view.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when map changes view.")]
        public string OnClientChangeView
        {
            get
            {
                return currentProperties.OnClientChangeView;
            }
            set
            {
                currentProperties.OnClientChangeView = value;
            }
        }

        /// <summary>
        /// This function receives notifications when map ends a pan
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when map ends a pan.")]
        public string OnClientEndPan
        {
            get
            {
                return currentProperties.OnClientEndPan;
            }
            set
            {
                currentProperties.OnClientEndPan = value;
            }
        }

        /// <summary>
        /// This function receives notifications when map ends a zoom
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when map ends a zoom.")]
        public string OnClientEndZoom
        {
            get
            {
                return currentProperties.OnClientEndZoom;
            }
            set
            {
                currentProperties.OnClientEndZoom = value;
            }
        }

        /// <summary>
        /// This function receives notifications when map errors
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when map errors.")]
        public string OnClientError
        {
            get
            {
                return currentProperties.OnClientError;
            }
            set
            {
                currentProperties.OnClientError = value;
            }
        }

        // Issue:27368 - Removed since this event is to be disabled/removed from the api
        ///// <summary>
        ///// This function receives notifications when map initilizes 3D mode
        ///// </summary>
        //[DefaultValue("")]
        //[Category(ControlCategory.ClientSideEvents)]
        //[Description("This function receives notifications when map initilizes 3D mode.")]
        //public string OnClientInitMode
        //{
        //    get
        //    {
        //        return currentProperties.OnClientInitMode;
        //    }
        //    set
        //    {
        //        currentProperties.OnClientInitMode = value;
        //    }
        //}

        /// <summary>
        /// This function receives notifications when 3D mode is not installed on client
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when 3D mode is not installed on client.")]
        public string OnClientModeNotAvailable
        {
            get
            {
                return currentProperties.OnClientModeNotAvailable;
            }
            set
            {
                currentProperties.OnClientModeNotAvailable = value;
            }
        }

        /// <summary>
        /// This function receives notifications when oblique (birdseye) image changes
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when oblique (birdseye) image changes.")]
        public string OnClientObliqueChange
        {
            get
            {
                return currentProperties.OnClientObliqueChange;
            }
            set
            {
                currentProperties.OnClientObliqueChange = value;
            }
        }

        /// <summary>
        /// This function receives notifications when oblique (birdseye) image loads
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when oblique (birdseye) image loads.")]
        public string OnClientObliqueEnter
        {
            get
            {
                return currentProperties.OnClientObliqueEnter;
            }
            set
            {
                currentProperties.OnClientObliqueEnter = value;
            }
        }

        /// <summary>
        /// This function receives notifications when oblique (birdseye) image exits
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when oblique (birdseye) image exits.")]
        public string OnClientObliqueLeave
        {
            get
            {
                return currentProperties.OnClientObliqueLeave;
            }
            set
            {
                currentProperties.OnClientObliqueLeave = value;
            }
        }

        /// <summary>
        /// This function receives notifications when map resizes
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when map resizes.")]
        public string OnClientResize
        {
            get
            {
                return currentProperties.OnClientResize;
            }
            set
            {
                currentProperties.OnClientResize = value;
            }
        }

        /// <summary>
        /// This function receives notifications when map begins a pan
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when map begins a pan.")]
        public string OnClientStartPan
        {
            get
            {
                return currentProperties.OnClientStartPan;
            }
            set
            {
                currentProperties.OnClientStartPan = value;
            }
        }

        /// <summary>
        /// This function receives notifications when map begins a zoom
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when map begins a zoom.")]
        public string OnClientStartZoom
        {
            get
            {
                return currentProperties.OnClientStartZoom;
            }
            set
            {
                currentProperties.OnClientStartZoom = value;
            }
        }

        /// <summary>
        /// This function receives notifications when key is pressed on the map
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when key is pressed on the map.")]
        public string OnClientKeyPress
        {
            get
            {
                return currentProperties.OnClientKeyPress;
            }
            set
            {
                currentProperties.OnClientKeyPress = value;
            }
        }

        /// <summary>
        /// This function receives notifications on down of key press on the map
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications on down of key press on the map.")]
        public string OnClientKeyDown
        {
            get
            {
                return currentProperties.OnClientKeyDown;
            }
            set
            {
                currentProperties.OnClientKeyDown = value;
            }
        }

        /// <summary>
        /// This function receives notifications on up of key press on the map.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications on up of key press on the map.")]
        public string OnClientKeyUp
        {
            get
            {
                return currentProperties.OnClientKeyUp;
            }
            set
            {
                currentProperties.OnClientKeyUp = value;
            }
        }

        /// <summary>
        /// This function receives notifications when mouse is clicked over the map.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when mouse is clicked over the map.")]
        public string OnClientClick
        {
            get
            {
                return currentProperties.OnClientClick;
            }
            set
            {
                currentProperties.OnClientClick = value;
            }
        }

        /// <summary>
        /// This function receives notifications when mouse is double clicked over the map.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when mouse is double clicked over the map.")]
        public string OnClientDoubleClick
        {
            get
            {
                return currentProperties.OnClientDoubleClick;
            }
            set
            {
                currentProperties.OnClientDoubleClick = value;
            }
        }

        /// <summary>
        /// This function receives notifications on mouse click down when mouse is clicked over the map.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications on mouse click down when mouse is clicked over the map.")]
        public string OnClientMouseDown
        {
            get
            {
                return currentProperties.OnClientMouseDown;
            }
            set
            {
                currentProperties.OnClientMouseDown = value;
            }
        }

        /// <summary>
        /// This function receives notifications when mouse moves over the map.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when mouse moves over the map.")]
        public string OnClientMouseMove
        {
            get
            {
                return currentProperties.OnClientMouseMove;
            }
            set
            {
                currentProperties.OnClientMouseMove = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when mouse leave the map or object on the map.")]
        public string OnClientMouseOut
        {
            get
            {
                return currentProperties.OnClientMouseOut;
            }
            set
            {
                currentProperties.OnClientMouseOut = value;
            }
        }

        /// <summary>
        /// This function receives notifications when mouse enters the map or object on the map.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when mouse enters the map or object on the map.")]
        public string OnClientMouseOver
        {
            get
            {
                return currentProperties.OnClientMouseOver;
            }
            set
            {
                currentProperties.OnClientMouseOver = value;
            }
        }

        /// <summary>
        /// This function receives notifications on mouse click up when mouse is clicked over the map.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications on mouse click up when mouse is clicked over the map.")]
        public string OnClientMouseUp
        {
            get
            {
                return currentProperties.OnClientMouseUp;
            }
            set
            {
                currentProperties.OnClientMouseUp = value;
            }
        }

        /// <summary>
        /// This function receives notifications when mouse wheel is used over the map.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when mouse wheel is used over the map.")]
        public string OnClientMouseWheel
        {
            get
            {
                return currentProperties.OnClientMouseWheel;
            }
            set
            {
                currentProperties.OnClientMouseWheel = value;
            }
        }

        /// <summary>
        /// This function receives notifications when a Find() call returns a result.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when a Find() call returns a result.")]
        public string OnClientFind
        {
            get
            {
                return currentProperties.OnClientFind;
            }
            set
            {
                currentProperties.OnClientFind = value;
            }
        }

        /// <summary>
        /// This function receives notifications when a GetDirections() call returns a result.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when a GetDirections() call returns a result.")]
        public string OnClientGetDirections
        {
            get
            {
                return currentProperties.OnClientGetDirections;
            }
            set
            {
                currentProperties.OnClientGetDirections = value;
            }
        }

        /// <summary>
        /// This function receives notifications when client token system receives an error.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when client token system receives an error.")]
        public string OnClientTokenError
        {
            get
            {
                return currentProperties.OnClientTokenError;
            }
            set
            {
                currentProperties.OnClientTokenError = value;
            }
        }

        /// <summary>
        /// This function receives notifications when client token system expires.
        /// </summary>
        [DefaultValue("")]
        [Category(ControlCategory.ClientSideEvents)]
        [Description("This function receives notifications when client token system expires.")]
        public string OnClientTokenExpire
        {
            get
            {
                return currentProperties.OnClientTokenExpire;
            }
            set
            {
                currentProperties.OnClientTokenExpire = value;
            }
        }

        #endregion

        #region Server Side Events
        protected virtual void OnServerLoadMap(EventArgs e)
        {
            EventHandler handler = Events[ServerOnLoadMapEventKey] as EventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerChangeMapStyle(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnChangeMapStyleEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerChangeView(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnChangeViewEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerEndPan(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnEndPanEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerEndZoom(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnEndZoomEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerError(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnErrorEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        // Issue:27368 - Removed since this event is to be disabled/removed from the api
        //protected virtual void OnServerInitMode(MapEventArgs e)
        //{
        //    MapEventHandler handler = Events[ServerOnInitModeEventKey] as MapEventHandler;

        //    if (handler != null)
        //    {
        //        handler(this, e);
        //    }
        //}

        protected virtual void OnServerModeNotAvailable(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnModeNotAvailableEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerObliqueChange(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnObliqueChangeEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerObliqueEnter(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnObliqueEnterEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerObliqueLeave(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnObliqueLeaveEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerResize(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnResizeEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerClick(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnClickEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerDoubleClick(MapEventArgs e)
        {
            MapEventHandler handler = Events[ServerOnDoubleClickEventKey] as MapEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerFind(FindEventArgs e)
        {
            MapFindEventHandler handler = Events[ServerOnFindEventKey] as MapFindEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerGetDirections(Route e)
        {
            MapRouteEventHandler handler = Events[ServerOnGetDirectionsEventKey] as MapRouteEventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerTokenError(EventArgs e)
        {
            EventHandler handler = Events[ServerOnTokenErrorEventKey] as EventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnServerTokenExpire(EventArgs e)
        {
            EventHandler handler = Events[ServerOnTokenExpireEventKey] as EventHandler;

            if (handler != null)
            {
                handler(this, e);
            }
        }


        #endregion

        #region Private Static Readonly Server Event Keys

        private static readonly object ServerOnLoadMapEventKey = new object();
        private static readonly object ServerOnChangeMapStyleEventKey = new object();
        private static readonly object ServerOnChangeViewEventKey = new object();
        private static readonly object ServerOnEndPanEventKey = new object();
        private static readonly object ServerOnEndZoomEventKey = new object();
        private static readonly object ServerOnErrorEventKey = new object();
        // Issue:27368 - Removed since this event is to be disabled/removed from the api
        //private static readonly object ServerOnInitModeEventKey = new object();
        private static readonly object ServerOnModeNotAvailableEventKey = new object();
        private static readonly object ServerOnObliqueChangeEventKey = new object();
        private static readonly object ServerOnObliqueEnterEventKey = new object();
        private static readonly object ServerOnObliqueLeaveEventKey = new object();
        private static readonly object ServerOnResizeEventKey = new object();
        private static readonly object ServerOnClickEventKey = new object();
        private static readonly object ServerOnDoubleClickEventKey = new object();
        private static readonly object ServerOnFindEventKey = new object();
        private static readonly object ServerOnGetDirectionsEventKey = new object();
        private static readonly object ServerOnTokenErrorEventKey = new object();
        private static readonly object ServerOnTokenExpireEventKey = new object();

        #endregion

        #region Public Instance Server Event(s)

        public event EventHandler ServerLoadMap
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerLoadMap, ServerOnLoadMapEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerLoadMap, ServerOnLoadMapEventKey, value);
            }
        }

        public event MapEventHandler ServerChangeMapStyle
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerChangeMapStyle, ServerOnChangeMapStyleEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerChangeMapStyle, ServerOnChangeMapStyleEventKey, value);
            }
        }

        public event MapEventHandler ServerChangeView
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerChangeView, ServerOnChangeViewEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerChangeView, ServerOnChangeViewEventKey, value);
            }
        }

        public event MapEventHandler ServerEndPan
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerEndPan, ServerOnEndPanEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerEndPan, ServerOnEndPanEventKey, value);
            }
        }

        public event MapEventHandler ServerEndZoom
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerEndZoom, ServerOnEndZoomEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerEndZoom, ServerOnEndZoomEventKey, value);
            }
        }

        public event MapEventHandler ServerError
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerError, ServerOnErrorEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerError, ServerOnErrorEventKey, value);
            }
        }

        // Issue:27368 - Removed since this event is to be disabled/removed from the api
        //public event MapEventHandler ServerInitMode
        //{
        //    add
        //    {
        //        addServerEvent(ScriptEvent.OnServerInitMode, ServerOnInitModeEventKey, value);
        //    }
        //    remove
        //    {
        //        removeServerEvent(ScriptEvent.OnServerInitMode, ServerOnInitModeEventKey, value);
        //    }
        //}

        public event MapEventHandler ServerModeNotAvailable
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerModeNotAvailable, ServerOnModeNotAvailableEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerModeNotAvailable, ServerOnModeNotAvailableEventKey, value);
            }
        }

        public event MapEventHandler ServerObliqueChange
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerObliqueChange, ServerOnObliqueChangeEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerObliqueChange, ServerOnObliqueChangeEventKey, value);
            }
        }

        public event MapEventHandler ServerObliqueEnter
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerObliqueEnter, ServerOnObliqueEnterEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerObliqueEnter, ServerOnObliqueEnterEventKey, value);
            }
        }

        public event MapEventHandler ServerObliqueLeave
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerObliqueLeave, ServerOnObliqueLeaveEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerObliqueLeave, ServerOnObliqueLeaveEventKey, value);
            }
        }

        public event MapEventHandler ServerResize
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerResize, ServerOnResizeEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerResize, ServerOnResizeEventKey, value);
            }
        }

        public event MapEventHandler ServerClick
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerClick, ServerOnClickEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerClick, ServerOnClickEventKey, value);
            }
        }

        public event MapEventHandler ServerDoubleClick
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerDoubleClick, ServerOnDoubleClickEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerDoubleClick, ServerOnDoubleClickEventKey, value);
            }
        }

        public event MapFindEventHandler ServerFind
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerFind, ServerOnFindEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerFind, ServerOnFindEventKey, value);
            }
        }

        public event MapRouteEventHandler ServerGetDirections
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerGetDirections, ServerOnGetDirectionsEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerGetDirections, ServerOnGetDirectionsEventKey, value);
            }
        }

        public event EventHandler ServerTokenError
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerTokenError, ServerOnTokenErrorEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerTokenError, ServerOnTokenErrorEventKey, value);
            }
        }

        public event EventHandler ServerTokenExpire
        {
            add
            {
                addServerEvent(ScriptEvent.OnServerTokenExpire, ServerOnTokenExpireEventKey, value);
            }
            remove
            {
                removeServerEvent(ScriptEvent.OnServerTokenExpire, ServerOnTokenExpireEventKey, value);
            }
        }

        private Dictionary<string, int> eventCount;


        #endregion

        #region Public Instance Delegate(s)

        public delegate void MapEventHandler(object sender, MapEventArgs e);
        public delegate void MapFindEventHandler(object sender, FindEventArgs e);
        public delegate void MapRouteEventHandler(object sender, Route e);

        #endregion

        #region IScriptControl Members

        protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            List<ScriptDescriptor> descriptors = new List<ScriptDescriptor>();

            ScriptControlDescriptor descriptor = new ScriptControlDescriptor("Microsoft.Live.ClientControls.VE.Map", ClientID);

            descriptor.AddProperty(ControlProperty.Height, Height.Value);
            descriptor.AddProperty(ControlProperty.Width, Width.Value);
            descriptor.AddProperty(ControlProperty.Center, Center);
            descriptor.AddProperty(ControlProperty.MapMode, MapMode);
            descriptor.AddProperty(ControlProperty.MapStyle, MapStyle.ToVEEnum());
            descriptor.AddProperty(ControlProperty.NavigationControl3D, NavigationControl3D);
            descriptor.AddProperty(ControlProperty.Dashboard, Dashboard);
            descriptor.AddProperty(ControlProperty.DashboardSize, DashboardSize.ToVEEnum());
            descriptor.AddProperty(ControlProperty.MiniMap, MiniMap);
            descriptor.AddProperty(ControlProperty.MiniMapXoffset, MiniMapXoffset);
            descriptor.AddProperty(ControlProperty.MiniMapYoffset, MiniMapYoffset);
            descriptor.AddProperty(ControlProperty.MiniMapSize, MiniMapSize.ToVEEnum());
            descriptor.AddProperty(ControlProperty.TrafficLegend, TrafficLegend);
            descriptor.AddProperty(ControlProperty.TrafficLegendX, TrafficLegendX);
            descriptor.AddProperty(ControlProperty.TrafficLegendY, TrafficLegendY);
            descriptor.AddProperty(ControlProperty.Traffic, Traffic);
            descriptor.AddProperty(ControlProperty.EnableShapeDisplayThreshold, EnableShapeDisplayThreshold);
            descriptor.AddProperty(ControlProperty.MouseWheelZoomToCenter, MouseWheelZoomToCenter);
            descriptor.AddProperty(ControlProperty.ScaleBarDistanceUnit, ScaleBarDistanceUnit.ToVEEnum());
            descriptor.AddProperty(ControlProperty.ShapesAccuracy, ShapesAccuracy);
            descriptor.AddProperty(ControlProperty.ShapesAccuracyRequestLimit, ShapesAccuracyRequestLimit);
            descriptor.AddProperty(ControlProperty.TileBuffer, TileBuffer);
            descriptor.AddProperty(ControlProperty.TrafficLegendText, TrafficLegendText);
            descriptor.AddProperty(ControlProperty.ZoomLevel, ZoomLevel);
            descriptor.AddProperty(ControlProperty.DisambiguationDialog, DisambiguationDialog);
            descriptor.AddProperty(ControlProperty.ClearInfoBoxStyles, ClearInfoBoxStyles);
            descriptor.AddProperty(ControlProperty.FixedMap, FixedMap);
            descriptor.AddProperty(ControlProperty.ShowMapModeSwitch, ShowMapModeSwitch);
            descriptor.AddProperty(ControlProperty.ShowFindControl, ShowFindControl);
            descriptor.AddProperty(ControlProperty.Altitude, Altitude);
            descriptor.AddProperty(ControlProperty.Heading, Heading);
            descriptor.AddProperty(ControlProperty.Pitch, Pitch);
            descriptor.AddProperty(ControlProperty.FailedShapeRequest, FailedShapeRequest);
            descriptor.AddProperty(ControlProperty.ClientToken, ClientToken);
            descriptor.AddProperty(ControlProperty.SendLayersToServer, SendLayersToServer);

            descriptor.AddProperty(ControlProperty.MapContainerID, MapContainer.ClientID);
            descriptor.AddProperty(ControlProperty.AsyncTriggerID, PanelTrigger.ClientID);
            descriptor.AddProperty(ControlProperty.MapDataID, MapData.ClientID);
            descriptor.AddProperty(ControlProperty.CommsFlagID, CommsFlag.ClientID);
            descriptor.AddProperty(ControlProperty.MapChangesID, MapChanges.ClientID);
            descriptor.AddProperty(ControlProperty.CommandsID, Commands.ClientID);

            ResourceManager rm = new ResourceManager("Microsoft.Live.ServerControls.VE.Map", Assembly.GetExecutingAssembly());

            descriptor.AddProperty(ControlProperty.BrowsersNotSupported, rm.GetString(ControlProperty.BrowsersNotSupported));
            descriptor.AddProperty(ControlProperty.VEAPINotFound, rm.GetString(ControlProperty.VEAPINotFound));

            //server side events
            foreach (KeyValuePair<string, int> pair in eventCount)
            {
                if (pair.Value > 0)
                {
                    descriptor.AddProperty(pair.Key, true);
                }
            }

            if (!string.IsNullOrEmpty(OnClientLoadMap.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientLoadMap, OnClientLoadMap);
            }
            if (!string.IsNullOrEmpty(OnClientChangeMapStyle.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientChangeMapStyle, OnClientChangeMapStyle);
            }
            if (!string.IsNullOrEmpty(OnClientChangeView.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientChangeView, OnClientChangeView);
            }
            if (!string.IsNullOrEmpty(OnClientEndPan.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientEndPan, OnClientEndPan);
            }
            if (!string.IsNullOrEmpty(OnClientEndZoom.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientEndZoom, OnClientEndZoom);
            }
            if (!string.IsNullOrEmpty(OnClientError.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientError, OnClientError);
            }
            // Issue:27368 - Removed since this event is to be disabled/removed from the api
            //if (!string.IsNullOrEmpty(OnClientInitMode.Trim()))
            //{
            //    descriptor.AddEvent(ScriptEvent.OnClientInitMode, OnClientInitMode);
            //}
            if (!string.IsNullOrEmpty(OnClientModeNotAvailable.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientModeNotAvailable, OnClientModeNotAvailable);
            }
            if (!string.IsNullOrEmpty(OnClientObliqueChange.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientObliqueChange, OnClientObliqueChange);
            }
            if (!string.IsNullOrEmpty(OnClientObliqueEnter.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientObliqueEnter, OnClientObliqueEnter);
            }
            if (!string.IsNullOrEmpty(OnClientObliqueLeave.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientObliqueLeave, OnClientObliqueLeave);
            }
            if (!string.IsNullOrEmpty(OnClientResize.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientResize, OnClientResize);
            }
            if (!string.IsNullOrEmpty(OnClientStartPan.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientStartPan, OnClientStartPan);
            }
            if (!string.IsNullOrEmpty(OnClientStartZoom.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientStartZoom, OnClientStartZoom);
            }
            if (!string.IsNullOrEmpty(OnClientKeyPress.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientKeyPress, OnClientKeyPress);
            }
            if (!string.IsNullOrEmpty(OnClientKeyDown.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientKeyDown, OnClientKeyDown);
            }
            if (!string.IsNullOrEmpty(OnClientKeyUp.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientKeyUp, OnClientKeyUp);
            }
            if (!string.IsNullOrEmpty(OnClientClick.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientClick, OnClientClick);
            }
            if (!string.IsNullOrEmpty(OnClientDoubleClick.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientDoubleClick, OnClientDoubleClick);
            }
            if (!string.IsNullOrEmpty(OnClientMouseDown.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientMouseDown, OnClientMouseDown);
            }
            if (!string.IsNullOrEmpty(OnClientMouseMove.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientMouseMove, OnClientMouseMove);
            }
            if (!string.IsNullOrEmpty(OnClientMouseOut.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientMouseOut, OnClientMouseOut);
            }
            if (!string.IsNullOrEmpty(OnClientMouseOver.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientMouseOver, OnClientMouseOver);
            }
            if (!string.IsNullOrEmpty(OnClientMouseUp.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientMouseUp, OnClientMouseUp);
            }
            if (!string.IsNullOrEmpty(OnClientMouseWheel.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientMouseWheel, OnClientMouseWheel);
            }
            if (!string.IsNullOrEmpty(OnClientFind.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientFind, OnClientFind);
            }
            if (!string.IsNullOrEmpty(OnClientGetDirections.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientGetDirections, OnClientGetDirections);
            }
            if (!string.IsNullOrEmpty(OnClientTokenError.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientTokenError, OnClientTokenError);
            }
            if (!string.IsNullOrEmpty(OnClientTokenExpire.Trim()))
            {
                descriptor.AddEvent(ScriptEvent.OnClientTokenExpire, OnClientTokenExpire);
            }

            descriptors.Add(descriptor);

            return descriptors;
        }

        protected override IEnumerable<ScriptReference> GetScriptReferences()
        {
            List<ScriptReference> descriptors = new List<ScriptReference>();
            //Detect protocol and apply HTTPS if required
            string URL;
            //validation: Must have client token, https selected and the request over https.
            bool useSecure = (ClientToken.Length > 0 && HTTPS && Page.Request.IsSecureConnection);

            if (useSecure)
            {
                URL = Constant.HTTPS;
            }else
            {
                URL = Constant.HTTP;
            }
            URL = URL + Constant.VEAPIURL;
            if (useSecure)
            {
                URL = URL + "&" + Constant.VESSLParam;
            }
            if (Locale != SupportedLocales.NA)
            {
                URL = URL + "&mkt=" + Locale.ToVEEnum();
            }
            descriptors.Add( new ScriptReference(URL));

            descriptors.Add(new ScriptReference("Microsoft.Live.ServerControls.VE.Map.js", GetType().Assembly.FullName));

            return descriptors;
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            //Set data to go to client
            MapChanges.Value = ReplaceEnumIntsWithStrings(propertyChangestoJSON());
            Commands.Value = ReplaceEnumIntsWithStrings(Actions.GetJSON());
            //determine if partial post back
            ScriptManager sm = ScriptManager.GetCurrent(Page);
            MapData.Value =ReplaceEnumIntsWithStrings( clientData.GetDataJSON(!sm.IsInAsyncPostBack || !SendLayersToServer));
            CommsFlag.Value = "1";
        }

        private static string ReplaceEnumIntsWithStrings(string serialisedData)
        {
            if (string.IsNullOrEmpty(serialisedData)) return string.Empty;

            string data = serialisedData;

            //replace enums with VEenums charactors where needed.
            data = data.Replace("\"AltitudeMode\":0", "\"AltitudeMode\":\"" + AltitudeMode.Default.ToVEEnum() + "\"");
            data = data.Replace("\"AltitudeMode\":1", "\"AltitudeMode\":\"" + AltitudeMode.Absolute.ToVEEnum() + "\"");
            data = data.Replace("\"AltitudeMode\":2", "\"AltitudeMode\":\"" + AltitudeMode.RelativeToGround.ToVEEnum() + "\"");

            data = data.Replace("\"Type\":0", "\"Type\":\"" + ShapeType.Pushpin.ToVEEnum() + "\"");
            data = data.Replace("\"Type\":1", "\"Type\":\"" + ShapeType.Polyline.ToVEEnum() + "\"");
            data = data.Replace("\"Type\":2", "\"Type\":\"" + ShapeType.Polygon.ToVEEnum() + "\"");

            return data;
        }

        private static string ReplaceEnumStringsWithInts(string serialisedData)
        {
            if (string.IsNullOrEmpty(serialisedData)) return string.Empty;
            string strJSON = serialisedData;
            //replace VEenums with enums values where needed.
            strJSON = strJSON.Replace("\"AltitudeMode\":\"" + AltitudeMode.Default.ToVEEnum() + "\"", "\"AltitudeMode\":0");
            strJSON = strJSON.Replace("\"AltitudeMode\":\"" + AltitudeMode.Absolute.ToVEEnum() + "\"", "\"AltitudeMode\":1");
            strJSON = strJSON.Replace("\"AltitudeMode\":\"" + AltitudeMode.RelativeToGround.ToVEEnum() + "\"", "\"AltitudeMode\":2");
            strJSON = strJSON.Replace("\"Type\":\"" + ShapeType.Pushpin.ToVEEnum() + "\"", "\"Type\":0");
            strJSON = strJSON.Replace("\"Type\":\"" + ShapeType.Polyline.ToVEEnum() + "\"", "\"Type\":1");
            strJSON = strJSON.Replace("\"Type\":\"" + ShapeType.Polygon.ToVEEnum() + "\"", "\"Type\":2");
            return strJSON;
        }


        /// <summary>
        /// Setup our Update panel and trigger for communication of data through some hidden Input fields.
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // Get a reference to the ScriptManager object for the page
            // if one exists.
            ScriptManager sm = ScriptManager.GetCurrent(Page);
            if (sm == null || !sm.EnablePartialRendering)
            {
                //Currently only support parital rendering enabled. It would be possible to support postbacks but not recommended for performance and user experience.
                ResourceManager rm = new ResourceManager("Microsoft.Live.ServerControls.VE.Map", Assembly.GetExecutingAssembly());
                throw new System.Exception(rm.GetString(ExceptionProperty.ExceptionScriptManager));
            }
            else
            {

                Controls.Clear();

                MapContainer = new Panel() { ID = "map" };
                UpdatePanel = new UpdatePanel() { ID = "updatePanel" };
                MapData = new HtmlInputHidden() { ID = "mapdata" };
                CommsFlag = new HtmlInputHidden() { ID = "comms" };
                MapChanges = new HtmlInputHidden() { ID = "mapchanges" };
                Commands = new HtmlInputHidden() { ID = "command" };
                PanelTrigger = new HiddenField() { ID = "panelTrigger" };

                UpdatePanel.Load += UpdatePanel_Load;

                Controls.Add(MapContainer);
                Controls.Add(MapData);
                Controls.Add(CommsFlag);
                Controls.Add(MapChanges);
                Controls.Add(Commands);

                UpdatePanel.RenderMode = UpdatePanelRenderMode.Block;
                UpdatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
                UpdatePanel.ChildrenAsTriggers = true;
                UpdatePanel.ContentTemplateContainer.Controls.Add(PanelTrigger);
                UpdatePanel.ContentTemplateContainer.Controls.Add(MapData);
                UpdatePanel.ContentTemplateContainer.Controls.Add(CommsFlag);
                UpdatePanel.ContentTemplateContainer.Controls.Add(MapChanges);
                UpdatePanel.ContentTemplateContainer.Controls.Add(Commands);

                AsyncPostBackTrigger postBackTrigger = new AsyncPostBackTrigger();
                postBackTrigger.ControlID = PanelTrigger.UniqueID;
                postBackTrigger.EventName = "ValueChanged";

                UpdatePanel.Triggers.Add(postBackTrigger);

                Controls.Add(UpdatePanel);

                SetPanelStyles();
            }
        }

        #endregion

        #region Private helpers

        private void addServerEvent(string skey, object key, Delegate value)
        {
            if (eventCount.ContainsKey(skey))
            {
                eventCount[skey] = eventCount[skey] + 1;
            }
            else
            {
                eventCount.Add(skey, 1);
            }
            Events.AddHandler(key, value);
        }

        private void removeServerEvent(string skey, object key, Delegate value)
        {
            if (eventCount.ContainsKey(skey))
            {
                eventCount[skey] = eventCount[skey] - 1;
            }
            Events.RemoveHandler(key, value);
        }

        private MapEventArgs getArgs(string param)
        {
            JavaScriptSerializer jss = new JavaScriptSerializer();
            if (param != null)
            {
                MapEventArgs args = jss.Deserialize<MapEventArgs>(param);
                if (args != null)
                {
                    return args;
                }
            }
            return new MapEventArgs();
        }

        private FindEventArgs getFindArgs(string param)
        {
            JavaScriptSerializer jss = new JavaScriptSerializer();
            if (param != null)
            {
                //Need to convert client side enum values into server side values before serialization
                param = param.Replace("\"Precision\":\"" + LocationPrecision.Interpolated.ToVEEnum() + "\"", "\"Precision\":0");
                param = param.Replace("\"Precision\":\"" + LocationPrecision.Rooftop.ToVEEnum() + "\"", "\"Precision\":1");
                param = param.Replace("\"MatchCode\":\"" + MatchCode.None.ToVEEnum() + "\"", "\"MatchCode\":0");
                param = param.Replace("\"MatchCode\":\"" + MatchCode.Good.ToVEEnum() + "\"", "\"MatchCode\":1");
                param = param.Replace("\"MatchCode\":\"" + MatchCode.Ambiguous.ToVEEnum() + "\"", "\"MatchCode\":2");
                param = param.Replace("\"MatchCode\":\"" + MatchCode.UpHierarchy.ToVEEnum() + "\"", "\"MatchCode\":4");
                param = param.Replace("\"MatchCode\":\"" + MatchCode.Modified.ToVEEnum() + "\"", "\"MatchCode\":8");
                //VE API documents this as an enum, values returned are doubles.
                //param = param.Replace("\"MatchConfidence\":\"" + MatchConfidence.High.ToVEEnum() + "\"", "\"MatchConfidence\":0");
                //param = param.Replace("\"MatchConfidence\":\"" + MatchConfidence.Medium.ToVEEnum() + "\"", "\"MatchConfidence\":1");
                //param = param.Replace("\"MatchConfidence\":\"" + MatchConfidence.Low.ToVEEnum() + "\"", "\"MatchConfidence\":2");
                param = param.Replace("\"Type\":\"" + ShapeType.Polygon.ToVEEnum() + "\"", "\"Type\":2");

                FindEventArgs args = jss.Deserialize<FindEventArgs>(param);
                if (args != null)
                {
                    return args;
                }
            }
            return new FindEventArgs();
        }

        private Route getRouteArgs(string param)
        {
            JavaScriptSerializer jss = new JavaScriptSerializer();
            if (param != null)
            {
                Route args = jss.Deserialize<Route>(param);
                if (args != null)
                {
                    return args;
                }
            }
            return new Route();
        }

        private void SetPanelStyles()
        {
            //ensure we have our minimal styles set.
            MapContainer.Style.Add("position", "relative");
            MapContainer.Style.Add("overflow", "hidden");

            //transfer the height and width
            MapContainer.Width = Width;
            MapContainer.Height = Height;
        }

        private string propertyChangestoJSON()
        {
            if (propertyChanges.Count > 0)
            {
                StringBuilder str = new StringBuilder();
                str.Append("{");
                foreach (KeyValuePair<string, string> pair in propertyChanges)
                {
                    str.Append("\"");
                    str.Append(pair.Key);
                    str.Append("\":");
                    str.Append(pair.Value);
                    str.Append(",");
                }
                return str.ToString().Substring(0, str.Length - 1) + "}";
            }
            return string.Empty;
        }

        private void SetPropertiesFromJSON(string strJSON)
        {
            JavaScriptSerializer jss = new JavaScriptSerializer();
            Dictionary<string, object> items = jss.Deserialize<Dictionary<string, object>>(strJSON);
            if (items != null)
            {
                foreach (KeyValuePair<string, object> item in items)
                {
                    string VEEnum;
                    switch (item.Key)
                    {
                        case ControlProperty.Height:
                            Height = new Unit(ConvertNullString(item.Value));
                            break;
                        case ControlProperty.Width:
                            Width = new Unit(ConvertNullString(item.Value));
                            break;
                        case ControlProperty.Dashboard:
                            Dashboard = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.DashboardSize:
                            VEEnum = ConvertNullString(item.Value);
                            if (VEEnum == VE.DashboardSize.Normal.ToVEEnum())
                            {
                                DashboardSize = VE.DashboardSize.Normal;
                            }
                            else if (VEEnum == VE.DashboardSize.Small.ToVEEnum())
                            {
                                DashboardSize = VE.DashboardSize.Small;
                            }
                            else if (VEEnum == VE.DashboardSize.Tiny.ToVEEnum())
                            {
                                DashboardSize = VE.DashboardSize.Tiny;
                            }
                            break;
                        case ControlProperty.DisambiguationDialog:
                            Dashboard = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.ClearInfoBoxStyles:
                            ClearInfoBoxStyles = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.EnableShapeDisplayThreshold:
                            EnableShapeDisplayThreshold = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.FixedMap:
                            FixedMap = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.Center:
                            Center = ConvertNullLatLong(item.Value);
                            break;
                        case ControlProperty.MapMode:
                            MapMode = (MapMode) item.Value;
                            break;
                        case ControlProperty.MapStyle:
                            VEEnum = ConvertNullString(item.Value);
                            if (VEEnum == VE.MapStyle.Aerial.ToVEEnum())
                            {
                                MapStyle = VE.MapStyle.Aerial;
                            }
                            else if (VEEnum == VE.MapStyle.Birdseye.ToVEEnum())
                            {
                                MapStyle = VE.MapStyle.Birdseye;
                            }
                                // Removed as no longer part of the API (in v6.1)
                            //else if (VEEnum == VE.MapStyle.Hybrid.ToVEEnum())
                            //{
                            //    MapStyle = VE.MapStyle.Hybrid;
                            //}
                            else if (VEEnum == VE.MapStyle.Oblique.ToVEEnum())
                            {
                                MapStyle = VE.MapStyle.Oblique;
                            }
                            else if (VEEnum == VE.MapStyle.Road.ToVEEnum())
                            {
                                MapStyle = VE.MapStyle.Road;
                            }
                            else if (VEEnum == VE.MapStyle.Shaded.ToVEEnum())
                            {
                                MapStyle = VE.MapStyle.Shaded;
                            }
                            break;
                        case ControlProperty.MiniMap:
                            MiniMap = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.MiniMapSize:
                            VEEnum = ConvertNullString(item.Value);
                            if (VEEnum == VE.MiniMapSize.Large.ToVEEnum())
                            {
                                MiniMapSize = VE.MiniMapSize.Large;
                            }
                            else if (VEEnum == VE.MiniMapSize.Small.ToVEEnum())
                            {
                                MiniMapSize = VE.MiniMapSize.Small;
                            }
                            break;
                        case ControlProperty.MiniMapXoffset:
                            MiniMapXoffset = ConvertNullInteger(item.Value);
                            break;
                        case ControlProperty.MiniMapYoffset:
                            MiniMapYoffset = ConvertNullInteger(item.Value);
                            break;
                        case ControlProperty.MouseWheelZoomToCenter:
                            MouseWheelZoomToCenter = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.NavigationControl3D:
                            NavigationControl3D = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.ScaleBarDistanceUnit:
                            VEEnum = ConvertNullString(item.Value);
                            if (VEEnum == VE.DistanceUnit.Kilometers.ToVEEnum())
                            {
                                ScaleBarDistanceUnit = VE.DistanceUnit.Kilometers;
                            }
                            else if (VEEnum == VE.DistanceUnit.Miles.ToVEEnum())
                            {
                                ScaleBarDistanceUnit = VE.DistanceUnit.Miles;
                            }
                            break;
                        case ControlProperty.ShapesAccuracy:
                            ShapesAccuracy = (ShapeAccuracy) item.Value;
                            break;
                        case ControlProperty.ShapesAccuracyRequestLimit:
                            ShapesAccuracyRequestLimit = ConvertNullInteger(item.Value);
                            break;
                        case ControlProperty.ShowMapModeSwitch:
                            ShowMapModeSwitch = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.TileBuffer:
                            TileBuffer = ConvertNullInteger(item.Value);
                            break;
                        case ControlProperty.Traffic:
                            Traffic = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.TrafficLegend:
                            TrafficLegend = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.TrafficLegendText:
                            TrafficLegendText = ConvertNullString(item.Value);
                            break;
                        case ControlProperty.TrafficLegendX:
                            TrafficLegendX = ConvertNullInteger(item.Value);
                            break;
                        case ControlProperty.TrafficLegendY:
                            TrafficLegendY = ConvertNullInteger(item.Value);
                            break;
                        case ControlProperty.ZoomLevel:
                            ZoomLevel = ConvertNullInteger(item.Value);
                            break;
                        case ControlProperty.ShowFindControl:
                            ShowFindControl = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.Altitude:
                            Altitude = ConvertNullDouble(item.Value);
                            break;
                        case ControlProperty.Heading:
                            Heading = ConvertNullDouble(item.Value);
                            break;
                        case ControlProperty.Pitch:
                            Pitch = ConvertNullDouble(item.Value);
                            break;
                        case ControlProperty.FailedShapeRequest:
                            FailedShapeRequest = (FailedShapeRequest) item.Value;
                            break;
                        case ControlProperty.IsBirdseyeAvailable:
                            isBirdseyeAvailable = ConvertNullBoolean(item.Value);
                            break;
                        case ControlProperty.CurrentBirdseyeScene:
                            BirdseyeScene birdseyeScene = new BirdseyeScene();
                            Dictionary<string, object> subitems = (Dictionary<string, object>)item.Value;
                            foreach (KeyValuePair<string, object> subitem in subitems)
                            {
                                switch (subitem.Key)
                                {
                                    case BirdseyeSceneControlProperty.BoundingRectangle:
                                        birdseyeScene.BoundingRectangle = ConvertNullLatLongRectangle(subitem.Value);
                                        break;
                                    case BirdseyeSceneControlProperty.Height:
                                        birdseyeScene.Height = ConvertNullInteger(subitem.Value);
                                        break;
                                    case BirdseyeSceneControlProperty.ID:
                                        birdseyeScene.ID = ConvertNullInteger(subitem.Value);
                                        break;
                                    case BirdseyeSceneControlProperty.Orientation:
                                        VEEnum = ConvertNullString(subitem.Value);
                                        if (VEEnum == Orientation.North.ToVEEnum())
                                        {
                                            birdseyeScene.Orientation = Orientation.North;
                                        }
                                        else if (VEEnum == Orientation.South.ToVEEnum())
                                        {
                                            birdseyeScene.Orientation = Orientation.South;
                                        }
                                        else if (VEEnum == Orientation.East.ToVEEnum())
                                        {
                                            birdseyeScene.Orientation = Orientation.East;
                                        }
                                        else if (VEEnum == Orientation.West.ToVEEnum())
                                        {
                                            birdseyeScene.Orientation = Orientation.West;
                                        }
                                        break;
                                    case BirdseyeSceneControlProperty.ThumbnailFilename:
                                        birdseyeScene.ThumbnailFilename = ConvertNullString(subitem.Value);
                                        break;
                                    case BirdseyeSceneControlProperty.Width:
                                        birdseyeScene.Width = ConvertNullInteger(subitem.Value);
                                        break;
                                }
                            }
                            currentBirdseyeScene = birdseyeScene;
                            break;
                        case ControlProperty.MapView:
                            mapView = ConvertNullLatLongRectangle(item.Value);
                            break;
                        case ControlProperty.SendLayersToServer:
                            SendLayersToServer = ConvertNullBoolean(item.Value);
                            break;
                    }
                }
            }
        }

        private static LatLongRectangle ConvertNullLatLongRectangle(object Field)
        {
            if (Field == null)
            {
                return null;
            }
            else
            {
                LatLongRectangle rect = new LatLongRectangle();
                Dictionary<string, object> subitems = (Dictionary<string, object>)Field;
                foreach (KeyValuePair<string, object> subitem in subitems)
                {
                    switch (subitem.Key)
                    {
                        case LatLongRectangleControlProperty.BottomLeftLatLong:
                            rect.BottomLeftLatLong = ConvertNullLatLong(subitem.Value);
                            break;
                        case LatLongRectangleControlProperty.BottomRightLatLong:
                            rect.BottomRightLatLong = ConvertNullLatLong(subitem.Value);
                            break;
                        case LatLongRectangleControlProperty.TopLeftLatLong:
                            rect.TopLeftLatLong = ConvertNullLatLong(subitem.Value);
                            break;
                        case LatLongRectangleControlProperty.TopRightLatLong:
                            rect.TopRightLatLong = ConvertNullLatLong(subitem.Value);
                            break;
                    }
                }
                return rect;
            }
        }

        private static LatLong ConvertNullLatLong(object Field)
        {
            if (Field == null)
            {
                return null;
            }
            else
            {
                LatLong latLong = new LatLong();
                Dictionary<string, object> subitems = (Dictionary<string, object>)Field;
                foreach (KeyValuePair<string, object> subitem in subitems)
                {
                    switch (subitem.Key)
                    {
                        case LatLongControlProperty.Latitude:
                            latLong.Latitude = ConvertNullDouble(subitem.Value);
                            break;
                        case LatLongControlProperty.Longitude:
                            latLong.Longitude = ConvertNullDouble(subitem.Value);
                            break;
                    }
                }
                return latLong;
            }
        }

        private static LatLongWithAltitude ConvertNullLatLongWithAltitude(object Field)
        {
            if (Field == null)
            {
                return null;
            }
            else
            {
                LatLongWithAltitude latLong = new LatLongWithAltitude();
                Dictionary<string, object> subitems = (Dictionary<string, object>)Field;
                foreach (KeyValuePair<string, object> subitem in subitems)
                {
                    switch (subitem.Key)
                    {
                        case LatLongControlProperty.Latitude:
                            latLong.Latitude = ConvertNullDouble(subitem.Value);
                            break;
                        case LatLongControlProperty.Longitude:
                            latLong.Longitude = ConvertNullDouble(subitem.Value);
                            break;
                        case LatLongControlProperty.Altitude:
                            latLong.Altitude = ConvertNullDouble(subitem.Value);
                            break;
                        case LatLongControlProperty.AltitudeMode:
                            string VEEnum = ConvertNullString(subitem.Value);
                            if (VEEnum == AltitudeMode.Absolute.ToVEEnum())
                            {
                                latLong.AltitudeMode = AltitudeMode.Absolute;
                            }
                            else if (VEEnum == AltitudeMode.Default.ToVEEnum())
                            {
                                latLong.AltitudeMode = AltitudeMode.Default;
                            }
                            else if (VEEnum == AltitudeMode.RelativeToGround.ToVEEnum())
                            {
                                latLong.AltitudeMode = AltitudeMode.RelativeToGround;
                            }
                            break;
                    }
                }
                return latLong;
            }
        }



        private static int ConvertNullInteger(object Field)
        {
            if (Field == null)
            {
                return 0;
            }
            else
            {
                return Convert.ToInt32(Field);
            }
        }

        private static double ConvertNullDouble(object Field)
        {
            if (Field == null)
            {
                return 0.0;
            }
            else
            {
                return Convert.ToDouble(Field);
            }
        }

        private static bool ConvertNullBoolean(object Field)
        {
            if (Field == null)
            {
                return false;
            }
            else
            {
                return Convert.ToBoolean(Field);
            }
        }

        private static string ConvertNullString(object Field)
        {
            if (Field == null)
            {
                return "";
            }
            else
            {
                return Convert.ToString(Field);
            }
        }

        #endregion
    }

 
}